package com.example.utils;

import com.example.annotation.Inject;
import com.example.model.BeanDefine;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by Administrator on 2016/12/31.
 */
public class ClassPathXMLApplicationContext {

    private List<BeanDefine> beanList = new ArrayList<>();
    private Map<String, Object> singletons = new HashMap<>();
    private static ClassPathXMLApplicationContext context =
            new ClassPathXMLApplicationContext("applicationContext.xml");

    public ClassPathXMLApplicationContext(String fileName) {

        // 读取配置文件管理的bean
        this.readXML(fileName);

        // 实例化bean
        this.instancesBean();

        // 注解处理器
        this.annotationInject();
    }

    public static ClassPathXMLApplicationContext getInstance() {
        return context;
    }


    public Object getBean(String beanId) {
        return singletons.get(beanId);
    }

    private void annotationInject() {
        for (String beanName : singletons.keySet()) {
            Object bean = singletons.get(beanName);
            if (bean != null) {
                this.propertyAnnotation(bean);
                this.fieldAnnotation(bean);
            }
        }
    }

    /**
     * 处理在字段上的注解
     * @param bean
     */
    private void fieldAnnotation(Object bean) {
        try {
            // 获取其全部的字段描述
            Field[] fields = bean.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field != null && field.isAnnotationPresent(Inject.class)) {
                    Inject inject = field.getAnnotation(Inject.class);
                    String name = "";
                    Object value = null;

                    if (inject.name() != null && !"".equals(inject.name())) {
                        name = inject.name();
                        value = singletons.get(name);
                    } else {
                        for (String key : singletons.keySet()) {

                            // 判断当前属性所属的类型是否在配置文件中存在
                            if (field.getType().isAssignableFrom(singletons.get(key).getClass())) {
                                // 获取类型匹配的实例对象
                                value = singletons.get(key);
                                break;
                            }
                        }
                    }

                    // 允许访问private字段
                    field.setAccessible(true);

                    // 把引用对象注入属性
                    field.set(bean, value);
                }

            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理在set方法加入的注解
     * @param bean
     */
    private void propertyAnnotation(Object bean) {
        try {
            // 获取其属性的描述
            PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();

            for (PropertyDescriptor p : ps) {
                // 获取所有set方法
                Method setter = p.getWriteMethod();

                // 判断set方法是否定义了注解
                if (setter != null && setter.isAnnotationPresent(Inject.class)) {
                    // 获取当前注解，并判断name属性是否为空
                    Inject inject = setter.getAnnotation(Inject.class);
                    String name = "";
                    Object value = null;

                    if (inject.name() != null && !"".equals(inject.name())) {
                        // 获取注解的name属性的内容
                        name = inject.name();
                        value = singletons.get(name);
                    } else { // 如果当前注解没有指定name属性，则根据类型进行匹配
                        for (String key : singletons.keySet()) {
                            // 判断当前属性所属的类是否在配置文件中存在
                            if (p.getPropertyType().isAssignableFrom(singletons.get(key).getClass())) {
                                // 获取类型匹配的实例对象
                                value = singletons.get(key);
                                break;
                            }
                        }
                    }

                    // 允许访问private方法
                    setter.setAccessible(true);

                    // 把引用对象注入属性
                    setter.invoke(bean, value);
                }
            }


        } catch (IntrospectionException
                | IllegalAccessException
                | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private void instancesBean() {
        for (BeanDefine bean : beanList) {
            try {
                singletons.put(bean.getId(), Class.forName(bean.getClassName()).newInstance());
            } catch (IllegalAccessException
                    | InstantiationException
                    | ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    private void readXML(String fileName) {
        Document document = null;
        SAXReader saxReader = new SAXReader();

        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            document = saxReader.read(classLoader.getResourceAsStream(fileName));

            Element beans = document.getRootElement();

            for (Iterator<Element> beansList = beans.elementIterator(); beansList.hasNext();) {
                Element element = beansList.next();
                BeanDefine bean = new BeanDefine(
                        element.attributeValue("id"),
                        element.attributeValue("class"));

                beanList.add(bean);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}
